package controller;

import gui.OptionPanel;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.List;

import javax.media.j3d.*;

import node.GeoNode;

import buildtools.BallConnector;
import buildtools.CircleBuilder;
import buildtools.Extractor;
import buildtools.StructureBuilder;

import com.sun.j3d.utils.picking.PickResult;

import component.AbstractComponent;
import component.Ball;
import component.ComponentFactory;
import component.Status;
import component.Structure;
import component.Tube;
import component.group.ComponentGroup;
import controller.selection.SelectionPanel;

public class SelectionController extends AbstractController {
	
	private ComponentGroup currentGroup = null;
	private Structure structure;
	private long clickStart;
	private ComponentFactory factory;
	
	private StructureBuilder builder = null;
	private OptionPanel optionPanel;
	private SelectionPanel selectionPanel = null;

	public SelectionController(Canvas3D canvas, BranchGroup pickableBranch, Structure structure, OptionPanel optionPanel, ComponentFactory factory) {
		super(canvas, pickableBranch);
		this.structure = structure;
		this.factory = factory;
		this.optionPanel = optionPanel;
		this.setupSelectionPanel();
	}
	
	private void setupSelectionPanel() {
		selectionPanel = new SelectionPanel();
		selectionPanel.getLockButton().addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				lockClicked();
			}
		});
		selectionPanel.getUnlockButton().addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				unlockClicked();
			}
		});
		selectionPanel.getTrashButton().addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				trashClicked();
			}
		});
	}
	
	private void trashClicked() {
		deleteSelection(currentGroup);
		this.removeAuto();
		updatePanel();
	}
	
	private void lockClicked() {
		if (currentGroup == null) return;
		currentGroup.lockAll();
		updatePanel();
	}
	
	private void unlockClicked() {
		if (currentGroup == null) return;
		currentGroup.unlockAll();
		updatePanel();
	}
	
	private void updatePanel() {
		selectionPanel.setCurrentGroup(currentGroup);
	}

	private void removeSelection() {
		if (currentGroup != null) {
			currentGroup.setStatus(Status.NORMAL);
			currentGroup.clear();
			currentGroup = null;
		}
		removeAuto();
		updatePanel();
	}

	private void selectComponent(AbstractComponent component) {
		ComponentGroup current = getCurrentGroup();
		if (current.contains(component))
			return;
		current.add(component);
		component.setStatus(Status.SELECTED, true);
		updatePossibleAuto();
		updatePanel();
	}
	
	private void removeAuto() {
		if (this.builder != null) {
			this.builder.clearTemporary();
			this.builder = null;
		}	
	}
	
	private void updatePossibleAuto() {
		removeAuto();
		ComponentGroup group = this.getCurrentGroup();
		if (group.isEmpty())
			return;
		if (group.size() == 1) {
			AbstractComponent component = group.getComponents().iterator().next();
			if (component instanceof Ball) {
				BallConnector ballConnector = new BallConnector(structure, factory);
				ballConnector.makeTubeConnectionsForBall((Ball)component);
				this.builder = ballConnector;
			}
		} else {
			List<Tube> tubes = Extractor.extractTubes(group);
			if (tubes.size() == 2) {
				CircleBuilder circleBuilder = new CircleBuilder(structure, factory);
				circleBuilder.makeStructureFromGroup(tubes);
				this.builder = circleBuilder;
			}
		}
	}

	private ComponentGroup getCurrentGroup() {
		if (currentGroup == null) {
			currentGroup = new ComponentGroup();
		}
		return currentGroup;
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent e) {
		if (e.getButton() == MouseEvent.BUTTON3) {
			clickStart  = e.getWhen();
		}
	}
	
	@Override
	public void mouseReleased(MouseEvent e) {
		
		if (e.getButton() == MouseEvent.BUTTON1) {
			PickResult pickResult = this.pickClosest(e);

			if (pickResult != null) {
				GeoNode object = (GeoNode)pickResult.getNode(PickResult.BRANCH_GROUP);
				AbstractComponent component = object.getComponent();
				System.out.println("Selected: " + object.debugInfo());
				if (structure.isPermanent(component)) {
					ComponentGroup s = getCurrentGroup();
					if (!s.isDirectlyConnected(component)) {
						removeSelection();
					}
					selectComponent(component);
				} else if (this.builder != null && builder.isTemporary(component)) {
					this.builder.temporaryClicked(component);
				}

			}
		}
		
		if (e.getButton() == MouseEvent.BUTTON3 && (e.getWhen() - clickStart < 250)){
			removeSelection();
		}

	}

	@Override
	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void activate() {
		
		optionPanel.setPanel(selectionPanel);
	}

	@Override
	public void deactivate() {
		removeSelection();
		optionPanel.clearPanel();
	}

	@Override
	public void keyPressed(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_ESCAPE:
			removeSelection();
			break;
		case KeyEvent.VK_DELETE:
			deleteSelection(currentGroup);
			this.removeAuto();
			break;
		}
		
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		// TODO Auto-generated method stub
		
	}

}
